/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package commons.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author Susanta Tewari
 * @since 1.0 Oct 26, 2010
 */
public class ArrayUtil {

    /**
     * Constructs ...
     *
     */
    private ArrayUtil() {}

    /**
     * Indices of elements in {@code list}, if present and in the order, they appear in the {@code sublist}.
     *
     * @param <T> list parameter type
     * @param list a list containing the collection {@code sublist}
     * @param sublist a collection from{@code list}
     * @return indices of the elements in {@code list} that are also present (by {@code equals}) in {@code subCollection}
     * is not contained in {@code list}
     * @see #get(java.util.List, int[])
     */
    public static <T> int[] indicesOf(List<T> list, List<T> sublist) {


        // null params
        if (list == null) {
            ExceptionUtil.throwIllegalArgExNull("list");
        }

        if (sublist == null) {
            ExceptionUtil.throwIllegalArgExNull("sublist");
        }


        // empty params
        if (list.isEmpty()) {

            ExceptionUtil.throwIllegalArgEx(ArrayUtil.class, "ArrayUtil_Collection_Is_Empty",
                                            "list");
        }

        if (sublist.isEmpty()) {

            ExceptionUtil.throwIllegalArgEx(ArrayUtil.class, "ArrayUtil_Collection_Is_Empty",
                                            "subList");
        }


        // sublist is sub list?
        if (!list.containsAll(sublist)) {
            ExceptionUtil.throwIllegalArgEx(ArrayUtil.class, "ArrayUtil_Not_A_SubList");
        }

        int[] result = new int[sublist.size()];

        for (int i = 0; i < result.length; i++) {


            // index is non-negative now
            int index = list.indexOf(sublist.get(i));

            result[i] = index;
        }

        return result;
    }

    /**
     * Expands {@link List#get(int)} to more number of elements.
     *
     * @param <T>
     * @param list
     * @param indices
     * @return
     * @see #indicesOf(java.util.List, java.util.List)
     */
    public static <T> List<T> get(List<T> list, int[] indices) {


        // null params
        if (list == null) {
            ExceptionUtil.throwIllegalArgExNull("list");
        }

        if (indices == null) {
            ExceptionUtil.throwIllegalArgExNull("indices");
        }


        // empty params
        if (list.isEmpty()) {

            ExceptionUtil.throwIllegalArgEx(ArrayUtil.class, "ArrayUtil_Collection_Is_Empty",
                                            "list");
        }

        if (indices.length == 0) {

            ExceptionUtil.throwIllegalArgEx(ArrayUtil.class, "ArrayUtil_Collection_Is_Empty",
                                            "indices");
        }

        List<T> result = new ArrayList<>(indices.length);

        for (int i = 0; i < indices.length; i++) {
            result.add(list.get(indices[i]));
        }

        return result;
    }

    /**
     * Does not change the order of the passed elements. Sorting is done using natural ordering.
     *
     * @param elements elements whose minimum is sought
     * @param <T> element type
     * @return index of the minimum element
     */
    public static <T> int indexOfMax(T[] elements) {

        final T[] clone = elements.clone();

        Arrays.sort(clone, Collections.reverseOrder());

        return Arrays.asList(elements).indexOf(clone[0]);
    }

    /**
     * @param data the array to be copied
     * @return a deep clone of the array
     */
    public static byte[][] clone(final byte[][] data) {

        byte[][] clone = new byte[data.length][];

        for (int i = 0; i < clone.length; i++) {
            clone[i] = data[i].clone();
        }

        return clone;
    }

    /**
     * Checks the array if all the rows are of the same length
     *
     * @param data a 2D int array to be checked
     * @return index of the first row not matching or -1 if all are equal
     */
    public static int hasEqualRowSize(int[][] data) {

        for (int i = 1; i < data.length; i++) {

            if (data[i].length != data[0].length) {
                return i;
            }
        }

        return -1;
    }

    /**
     * Checks the array if all the rows are of the same length
     *
     * @param data a 2D int array to be checked
     * @return index of the first row not matching or -1 if all are equal
     */
    public static int hasEqualRowSize(byte[][] data) {

        for (int i = 1; i < data.length; i++) {

            if (data[i].length != data[0].length) {
                return i;
            }
        }

        return -1;
    }

    /**
     * Checks the array for duplicate rows
     *
     * @param data a 2D int array to be checked
     * @return index of the first duplicate row or -1 if none is found
     */
    public static int hasDupicateRows(int[][] data) {

        for (int i = 0; i < data.length; i++) {

            for (int j = i + 1; j < data.length; j++) {

                if (Arrays.equals(data[i], data[j])) {
                    return j;
                }
            }
        }

        return -1;
    }

    /**
     * Checks the array for duplicate rows
     *
     * @param data a 2D int array to be checked
     * @return index of the first duplicate row or -1 if none is found
     */
    public static int hasDupicateRows(byte[][] data) {

        for (int i = 0; i < data.length; i++) {

            for (int j = i + 1; j < data.length; j++) {

                if (Arrays.equals(data[i], data[j])) {
                    return j;
                }
            }
        }

        return -1;
    }

    /**
     * Method description
     *
     *
     * @param array
     * @param colIndex
     *
     * @return
     */
    public static int[] getColumn(int[][] array, int colIndex) {

        int[] col = new int[array.length];

        for (int i = 0; i < array.length; i++) {
            col[i] = array[i][colIndex];
        }

        return col;
    }

    public static <T> T[] getColumn(T[][] array, int colIndex) {

        T[] col = (T[]) new Object[array.length];

        for (int i = 0; i < array.length; i++) {
            col[i] = array[i][colIndex];
        }

        return col;
    }

    /**
     * @param data the array to be copied
     * @return a deep clone of the array
     */
    public static int[][] clone(final int[][] data) {

        int[][] clone = new int[data.length][];

        for (int i = 0; i < clone.length; i++) {
            clone[i] = data[i].clone();
        }

        return clone;
    }

    /**
     * Creates a new array that is the transpose of the given array
     *
     * @param array
     * @return
     */
    public static byte[][] transpose(byte[][] array) {

        byte[][] result = array.clone();

        for (int i = 0; i < array[0].length; i++) {

            for (int j = 0; j < array.length; j++) {
                result[i][j] = array[j][i];
            }
        }

        return result;
    }

    /**
     * Creates a sequence of length n
     *
     * @param n the length of the sequence
     * @param startAtZero
     * @return a new array containing the sequence
     */
    public static int[] createSequence(int n, boolean startAtZero) {

        int[] temp = new int[n];
        int start  = 1;

        if (startAtZero) {
            start = 0;
        }

        for (int index = 0; index < temp.length; index++) {

            temp[index] = start;

            start++;
        }

        return temp;
    }

    /**
     * Method description
     *
     *
     * @param src
     * @param dest
     * @param indices
     * @param <T>
     * @deprecated Imprecise specification, will be removed
     */
    public static <T> void selectedIndices(List<T> src, List<T> dest, int[] indices) {

        for (int i = 0; i < indices.length; i++) {
            dest.set(indices[i], src.get(i));
        }
    }
}
